﻿using WPay.Application.Interface.OAuth;
using WPay.Domain.Model.DTOs;
using WPay.Infrastructure.Unity;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Org.BouncyCastle.Crypto.Tls;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Nancy.Json;

namespace WPay.Web.API
{
    /// <summary>
    /// Token验证授权中间件
    /// </summary>
    public class TokenAuth
    {
        /// <summary>
        /// http委托
        /// </summary>
        private readonly RequestDelegate _next;
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="next"></param>
        public TokenAuth(RequestDelegate next)
        {
            _next = next;
        }
        /// <summary>
        /// 验证授权
        /// </summary>
        /// <param name="httpContext">Http请求上下文</param>
        /// <param name="_token_ApplicationService">token应用服务接口</param>
        /// <returns></returns>
        public Task Invoke(HttpContext httpContext)
        {
            var headers = httpContext.Request.Headers;
            var tokenStr = "";
            //检测是否包含'Authorization'请求头，如果不包含返回context进行下一个中间件，用于访问不需要认证的API
            if (!headers.ContainsKey("Authorization"))
            {
                return _next(httpContext);
            }
            try
            {
                tokenStr = headers["Authorization"];
                //验证缓存中是否存在该jwt字符串
                if (string.IsNullOrEmpty(tokenStr))
                {
                    CommonUtility.LoggerHelper.Log($"页面tokenStr---{tokenStr}");
                    #region 若是多应用 使用单独token 服务器 则可进行双边验证(即优先验证本地 若本地不存在则验证服务器(服务器缓存时间大于本地))
                    //您的业务代码
                    #endregion
                    return httpContext.Response.WriteAsync(JSONSerializer.Serialize(new RespDto(false, 401, "登录信息已过期,请退出重新登录!")));
                }
                var now = DateTime.Now;

                JwtSecurityToken jwtToken = null;
                jwtToken = new JwtSecurityTokenHandler().ReadJwtToken(tokenStr);
                if (jwtToken == null)
                {
                    CommonUtility.LoggerHelper.Log($"jwtToken过期===={tokenStr}");
                    return httpContext.Response.WriteAsync(JSONSerializer.Serialize(new RespDto(false, 401, "登录信息已过期,请退出重新登录!")));
                }
                var loginid = "";
                jwtToken.Claims.ToList().ForEach(x =>
                {
                    if (x.Type == "Uid")
                    {
                        loginid = x.Value;
                    }
                });
                TokenModel tm = RayPIMemoryCache.Get<TokenModel>(loginid);
                //判断主机请求域是否和获取 redistoken 时保存域相同
                if (httpContext.Request.Headers["Origin"] != tm.Origin)
                {
                    return httpContext.Response.WriteAsync(JSONSerializer.Serialize(new RespDto(false, 401, "非法请求,您无权访问!")));
                }
                //提取tokenModel中的Sub属性进行authorize认证
                List<Claim> lc = new List<Claim>();
                Claim c = new Claim(tm.Sub + "Type", tm.Sub);
                lc.Add(c);
                ClaimsIdentity identity = new ClaimsIdentity(lc);
                ClaimsPrincipal principal = new ClaimsPrincipal(identity);
                httpContext.User = principal;
                return _next(httpContext);
            }
            catch (Exception e)
            {
                CommonUtility.LoggerHelper.Log($"TokenModel---tokenStr：{tokenStr} Error{e.Message + e.InnerException?.Message}");
                return httpContext.Response.WriteAsync(JSONSerializer.Serialize(new RespDto(false, 500, "服务端验证错误,请重新登录!")));
            }
        }
    }
}
